package nachos.vm;

import nachos.machine.*;
import nachos.threads.*;
import nachos.userprog.*;
import nachos.vm.*;

import java.util.*;

public class Swap
{
	public Swap()
	{
		swapFile = Machine.stubFileSystem().open("swap", true);
        pageTable = kernel.pageTable;
	}

	/*
	 * Write page to swapFile
	 */
	public boolean out(int ppn, int pid, int vpn)
	{
		Integer pos = lookup.get(PageTable.makeOwner(pid, vpn));

		if(pos == null)
		{
			if(freePages.size() > 0)
			{
				pos = freePages.removeFirst();
			}
			else
			{
				usedPages.add(PageTable.makeOwner(pid, vpn));
				pos = usedPages.size() - 1;
			}
		}
		
		int bytes = swapFile.write(pos * pageSize, Machine.processor().getMemory(), ppn * pageSize, pageSize);

		if(bytes != pageSize)
			return false;

		usedPages.set(pos, PageTable.makeOwner(pid, vpn));
		lookup.put(PageTable.makeOwner(pid, vpn), pos);
		
		// TODO: Remove entry from VMProcess.replacementList

		return true;
	}

	/*
	 * Read page from swapFile into main memory
	 */
	public boolean in(int ppn, int pid, int vpn)
	{
		Integer pos = lookup.get(PageTable.makeOwner(pid, vpn));

		if(pos == null)
		{
			return false;
		}
		else
		{
			int bytes = swapFile.read(pos * pageSize, Machine.processor().getMemory(), ppn * pageSize, pageSize);

			if(bytes != pageSize)
				return false;
		}
		
		// Add new page to pageTable and recplacementList linked list
		TranslationEntry t = new TranslationEntry(vpn, ppn, true, false, false, false);
		//pageTable.addEntry(pid, t);
		//pageTable.replacementListIt.add(t);

		return true;
	}
    
    public boolean contains(int pid, int vpn)
    {
        return lookup.containsKey(PageTable.makeOwner(pid, vpn));
    }

	protected static VMKernel kernel = (VMKernel)VMKernel.kernel;
	protected PageTable pageTable;
	private static final int pageSize = Processor.pageSize;
	protected LinkedList<Integer> freePages = new LinkedList();
	protected ArrayList<Long> usedPages = new ArrayList<Long>();
	protected Hashtable<Long,Integer> lookup = new Hashtable<Long,Integer>();
	protected OpenFile swapFile = null;
}
